package com.ng.framework.persistence;

import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.transaction.annotation.Transactional;

public abstract class NGGenericPersistenceJPA<T> extends NGBasePersistence implements NGGenericPersistence<T> {
	private final Class<T> clasePersistente;

	@SuppressWarnings("unchecked")
	public NGGenericPersistenceJPA() {
		this.clasePersistente = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public Class<T> getClasePersistente() {
		return this.clasePersistente;
	}

	@Override
	public T findById(final Long id) {
		T entity;
		entity = (T) getHibernateTemplate().get(this.getClasePersistente(), id);
		return entity;
	}

	public List<T> getAll(final NGSearchCriteria criteria) {
		if (null == criteria || criteria.getBPSearchParametersSize() == 0) {
			return this.getAll();
		}
		final StringBuilder queryHQL = new StringBuilder();

		queryHQL.append("SELECT DISTINCT " + this.getClasePersistente().getSimpleName());
		queryHQL.append(" FROM " + this.getClasePersistente().getName());
		queryHQL.append(" " + this.getClasePersistente().getSimpleName());

		queryHQL.append(" " + criteria.getFrom());

		queryHQL.append(" WHERE ");

		queryHQL.append(criteria.getWhere());

		return getHibernateTemplate().find(queryHQL.toString());

	}

	@Override
	@Transactional()
	public boolean checkIfTableExists(String tableName) {

		Session session = this.getSession();

		return session.createSQLQuery("SHOW TABLES LIKE '" + tableName + "'").list().size() > 0;
	}

	@Transactional(readOnly = true)
	public List<T> getAll(final String hql) {
		return getHibernateTemplate().find(hql);

	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> getAll() {
		return getHibernateTemplate().find("FROM " + this.getClasePersistente().getName());
	}

	@Override
	public void remove(final T object) {
		this.getHibernateTemplate().delete(object);
	}

	@Override
	public T create(final T object) {
		this.getHibernateTemplate().save(object);
		return object;
	}

	@Override
	public T update(final T object) {
		this.getHibernateTemplate().saveOrUpdate(object);
		return object;
	}

	private class HibernateCallbackImpl implements HibernateCallback<List<T>> {

		private String queryString;
		private String[] paramNames;
		private Object[] values;

		private int firstResult;
		private int maxResults;

		/**
		 * Fetches a {@link List} of entities from the database using pagination. Execute HQL query, binding a number of values to ":" named parameters in the query string.
		 * 
		 * @param queryString
		 *            a query expressed in Hibernate's query language
		 * @param paramNames
		 *            the names of the parameters
		 * @param values
		 *            the values of the parameters
		 * @param firstResult
		 *            a row number, numbered from 0
		 * @param maxResults
		 *            the maximum number of rows
		 */
		public HibernateCallbackImpl(String queryString, String[] paramNames, Object[] values, int firstResult, int maxResults) {
			this.queryString = queryString;
			this.paramNames = paramNames;
			this.values = values;

			this.firstResult = firstResult;
			this.maxResults = maxResults;
		}

		@Override
		public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
			Query query = session.createQuery(queryString);
			query.setFirstResult(firstResult);
			query.setMaxResults(maxResults);

			@SuppressWarnings("unchecked")
			List<T> result = query.list();

			return result;
		}

	}
}
